<!DOCTYPE html>




<html class="theme-next gemini" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.3" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.3">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.3">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.3">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.3" color="#222">





  <meta name="keywords" content="Android插件化,Replugin," />





  <link rel="alternate" href="/atom.xml" title="流水不腐小夏" type="application/atom+xml" />






<meta name="description" content="是时候来一波Android插件化了   是时候来一波Android插件化了 前言 Android开发演进 模块化介绍 插件化介绍 前提技术介绍 APK构成 Manifest Application 四大组件 so resource 安装路径   App启动流程介绍 IPC &amp;amp; Binder AMS   插件化技术问题与解决方案 代码加载 Java ClassLoader Android C">
<meta name="keywords" content="Android插件化,Replugin">
<meta property="og:type" content="article">
<meta property="og:title" content="是时候来一波Android插件化了">
<meta property="og:url" content="http://mycommons.cn/2017/10/25/Android-RePlugin/index.html">
<meta property="og:site_name" content="流水不腐小夏">
<meta property="og:description" content="是时候来一波Android插件化了   是时候来一波Android插件化了 前言 Android开发演进 模块化介绍 插件化介绍 前提技术介绍 APK构成 Manifest Application 四大组件 so resource 安装路径   App启动流程介绍 IPC &amp;amp; Binder AMS   插件化技术问题与解决方案 代码加载 Java ClassLoader Android C">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-1a38c2a3efdf8bbb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-48e72c0042eaac1e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-8e9ae535efc19490.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-aa479b31cdbeecf5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-7232a81c66f98a47.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="https://raw.githubusercontent.com/LiushuiXiaoxia/AndroidPluginArticle/master/doc/6.png">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-5e31f0795f7befc0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-f78caf0b6a8668fe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-af48722dca715bea.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-6821aef3d92c294a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-7c8316e2a4fdf48c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-1cd50f773eacea05.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="https://raw.githubusercontent.com/LiushuiXiaoxia/AndroidPluginArticle/master/doc/903.png">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-44dd1a8a5e8dfa44.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/1520343-b33e1015da5c2652.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:updated_time" content="2017-10-25T10:47:50.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="是时候来一波Android插件化了">
<meta name="twitter:description" content="是时候来一波Android插件化了   是时候来一波Android插件化了 前言 Android开发演进 模块化介绍 插件化介绍 前提技术介绍 APK构成 Manifest Application 四大组件 so resource 安装路径   App启动流程介绍 IPC &amp;amp; Binder AMS   插件化技术问题与解决方案 代码加载 Java ClassLoader Android C">
<meta name="twitter:image" content="http://upload-images.jianshu.io/upload_images/1520343-1a38c2a3efdf8bbb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '5.1.3',
    sidebar: {"position":"left","display":"always","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":false,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: 'GRLHBBCD9E',
      apiKey: '759ab3f5c09b132b3ed4842a44c53c45',
      indexName: 'mycommons',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://mycommons.cn/2017/10/25/Android-RePlugin/"/>





  <title>是时候来一波Android插件化了 | 流水不腐小夏</title>
  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?55458ffca1f60dd283f11b9a7f82b262";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">流水不腐小夏</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <h1 class="site-subtitle" itemprop="description">http://mycommons.cn</h1>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-commonweal">
          <a href="/404.html" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-heartbeat"></i> <br />
            
            公益404
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  
  <div class="algolia-popup popup search-popup">
    <div class="algolia-search">
      <div class="algolia-search-input-icon">
        <i class="fa fa-search"></i>
      </div>
      <div class="algolia-search-input" id="algolia-search-input"></div>
    </div>

    <div class="algolia-results">
      <div id="algolia-stats"></div>
      <div id="algolia-hits"></div>
      <div id="algolia-pagination" class="algolia-pagination"></div>
    </div>

    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
  </div>




    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://mycommons.cn/2017/10/25/Android-RePlugin/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="流水不腐小夏">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.jpeg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="流水不腐小夏">
    </span>

    
      <header class="post-header">

        
        
          <h2 class="post-title" itemprop="name headline">是时候来一波Android插件化了</h2>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-10-25T18:46:55+08:00">
                2017-10-25
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index">
                    <span itemprop="name">Android</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
              <span class="post-comments-count">
                <span class="post-meta-divider">|</span>
                <span class="post-meta-item-icon">
                  <i class="fa fa-comment-o"></i>
                </span>
                <a href="/2017/10/25/Android-RePlugin/#comments" itemprop="discussionUrl">
                  <span class="post-comments-count disqus-comment-count"
                        data-disqus-identifier="2017/10/25/Android-RePlugin/" itemprop="commentCount"></span>
                </a>
              </span>
            
          

          
          

          
            <span class="post-meta-divider">|</span>
            <span class="page-pv">本文总阅读量
            <span class="busuanzi-value" id="busuanzi_value_page_pv" ></span>次
            </span>
          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h1 id="是时候来一波Android插件化了"><a href="#是时候来一波Android插件化了" class="headerlink" title="是时候来一波Android插件化了"></a>是时候来一波Android插件化了</h1><hr>
<!-- TOC -->
<ul>
<li><a href="#是时候来一波android插件化了">是时候来一波Android插件化了</a><ul>
<li><a href="#前言">前言</a></li>
<li><a href="#android开发演进">Android开发演进</a></li>
<li><a href="#模块化介绍">模块化介绍</a></li>
<li><a href="#插件化介绍">插件化介绍</a></li>
<li><a href="#前提技术介绍">前提技术介绍</a><ul>
<li><a href="#apk构成">APK构成</a><ul>
<li><a href="#manifest">Manifest</a></li>
<li><a href="#application">Application</a></li>
<li><a href="#四大组件">四大组件</a></li>
<li><a href="#so">so</a></li>
<li><a href="#resource">resource</a></li>
<li><a href="#安装路径">安装路径</a></li>
</ul>
</li>
<li><a href="#app启动流程介绍">App启动流程介绍</a><ul>
<li><a href="#ipc--binder">IPC &amp; Binder</a></li>
<li><a href="#ams">AMS</a></li>
</ul>
</li>
<li><a href="#插件化技术问题与解决方案">插件化技术问题与解决方案</a><ul>
<li><a href="#代码加载">代码加载</a><ul>
<li><a href="#java-classloader">Java ClassLoader</a></li>
<li><a href="#android-classloader">Android ClassLoader</a><ul>
<li><a href="#pathclassloader">PathClassLoader</a></li>
<li><a href="#dexclassloader">DexClassLoader</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#资源获取">资源获取</a></li>
<li><a href="#hook">Hook</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#主流框架方案">主流框架方案</a><ul>
<li><a href="#fragment加载">Fragment加载</a></li>
<li><a href="#activity代理">Activity代理</a></li>
<li><a href="#activity占坑">Activity占坑</a></li>
</ul>
</li>
<li><a href="#360replugin介绍">360RePlugin介绍</a><ul>
<li><a href="#主要优势">主要优势</a></li>
<li><a href="#集成与demo演示">集成与Demo演示</a></li>
<li><a href="#原理介绍">原理介绍</a><ul>
<li><a href="#host-lib">host lib</a></li>
<li><a href="#host-gradle">host gradle</a></li>
<li><a href="#plugin-lib">plugin lib</a></li>
<li><a href="#plugin-gradle">plugin gradle</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#其他插件化方案">其他插件化方案</a><ul>
<li><a href="#instant-app">Instant App</a></li>
<li><a href="#淘宝atlas">淘宝Atlas</a></li>
<li><a href="#滴滴virtualapk">滴滴VirtualAPK</a></li>
<li><a href="#small">Small</a></li>
</ul>
</li>
<li><a href="#总结">总结</a></li>
<li><a href="#相关资料">相关资料</a></li>
</ul>
</li>
</ul>
<!-- /TOC -->
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>今年（2017年）6月时候，有幸参加了在北京举行的GMTC大会，恰巧360的张炅轩大神分享了360的插件化方案—— <a href="https://github.com/Qihoo360/RePlugin" target="_blank" rel="external">RePlugin</a> ，听了以后，受益匪浅。</p>
<p>因为是公司组织参加大会的，参会后需要技术分享，所以就选择介绍RePlugin以及Android插件化相关内容，本文也是主要介绍RePlugin以及自己对插件化的理解。</p>
<p>因为插件化涉及到的东西比较多，由于篇幅的限制，很多知识点只是简单介绍一下，同时会给出相关链接，读者可以点击作参考。</p>
<p>这几年，世面上就已经出现了不少几款插件化方案，同时热更新技术也是遍地开花。当时是比较抵触这类技术的，个人觉的这样会破坏Android的生态圈，但是毕竟出现了这么多的插件化方案，出现总是有道理的。本着学习的态度，还是要学习下插件化相关技术。</p>
<h2 id="Android开发演进"><a href="#Android开发演进" class="headerlink" title="Android开发演进"></a>Android开发演进</h2><p>Android开发初期，基本上没有什么框架的，什么东西都往Activity里面塞，最后Activity就变得很大。后面有些人借鉴了Java后端的思想，使用MVC模式，一定程度上解决了代码乱堆的问题，<br>使用了一段时间MVC后，Activity依旧变的很大，因为Activity里面不光有UI的逻辑，还有数据的逻辑。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-1a38c2a3efdf8bbb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="MVC"></p>
<p>再后来有了MVP，MVP解决了UI逻辑和数据逻辑在一起的问题，同时也解决了Android代码测试困难问题。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-48e72c0042eaac1e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="MVP"></p>
<p>随着业务的增多，架构中有了Domain的概念，Domain从Data中获取数据，Data可能会是Net，File，Cache各种IO等，然后项目架构变成了这样。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-8e9ae535efc19490.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="MVP2"></p>
<h2 id="模块化介绍"><a href="#模块化介绍" class="headerlink" title="模块化介绍"></a>模块化介绍</h2><p>MVP升级版用了一段时间以后，新问题又出现了。随着业务的增多，代码变的越来越复杂，每个模块之间的代码耦合变得越来越严重，解耦问题急需解决，同时编译时间也会越来越长。</p>
<p>开发人员增多，每个业务的组件各自实现一套，导致同一个App的UI风格不一样，技术实现也不一样，团队技术也无法得到沉淀，重复早轮子严重。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-aa479b31cdbeecf5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Modular"></p>
<p>然后模块化（组件化）解决方案就出现了。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-7232a81c66f98a47.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Modular2"></p>
<h2 id="插件化介绍"><a href="#插件化介绍" class="headerlink" title="插件化介绍"></a>插件化介绍</h2><p>讲道理，模块化已经是最终完美的解决方案了，为啥还需要插件化呢？</p>
<p>还是得从业务说起，如果一个公司有很多业务，并且每个业务可以汇总成一个大的App，又或者某一个小业务又需要单独做成一个小的App。</p>
<p>按照上面的说的模块化解决方案，需要把这个业务设计成一个模块，代码最终打包成一个aar，主App和业务App设计成一个运行壳子，编译打包时候使用Gradle做maven依赖即可。</p>
<p>举例说明美团和猫眼电影。</p>
<p><img src="https://raw.githubusercontent.com/LiushuiXiaoxia/AndroidPluginArticle/master/doc/6.png" alt="美团和猫眼"></p>
<p>实际上这样做比较麻烦，主App和业务模块会或多或少依赖一点公共代码，如果公共代码出现变动，则需要对应做出修改。<br>同时业务代码会设计成Android Lib project，开发、编译、调试也有点麻烦，那么能不能这样设计，某个业务模块单独做出一个Apk，主App直接使用插件的方式，如果需要某种功能，那么直接加载某一个apk，而不是直接依赖代码的形式。</p>
<h2 id="前提技术介绍"><a href="#前提技术介绍" class="headerlink" title="前提技术介绍"></a>前提技术介绍</h2><p>通过上面的业务演进，最终我们需要做的就是一个Apk调用另外一个Apk文件，这也就是我们今天的主题——插件化。</p>
<p>一个常识，大家都知道，Apk只有在安装的情况下，才可以被运行调用。如果一个Apk只是一个文件，放置在存储卡上，我们如何才能调用起来呢？</p>
<p>对于这个问题，先保留，后面会做讲解，当然了已经有几种方案是可以这样做的。但是为了了解插件化的原理，先回顾一下基础知识。</p>
<h3 id="APK构成"><a href="#APK构成" class="headerlink" title="APK构成"></a>APK构成</h3><p>Apk是App代码最终编译打包生成的文件，主要包含代码（dex、so）、配置文件、资源问题、签名校验等。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-5e31f0795f7befc0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<h4 id="Manifest"><a href="#Manifest" class="headerlink" title="Manifest"></a>Manifest</h4><p>App中系统组件配置文件，包括Application、Activity、Service、Receiver、Provider等。</p>
<p>App中所有可运行的Activity必须要在这里定义，否则就不能运行，也包括其他组件，Receiver也可以动态注册。（敲黑板，这里很重要，记住这句话。）</p>
<h4 id="Application"><a href="#Application" class="headerlink" title="Application"></a>Application</h4><p>App启动，代码中可以获取到被运行调用的第一个类，常用来做一些初始化操作。</p>
<h4 id="四大组件"><a href="#四大组件" class="headerlink" title="四大组件"></a>四大组件</h4><p>四大系统组件Activity、Service、Receiver、Provider，代码中继承系统中的父类。如上面所说，必须要在manifest中配置定义，否则不可以被调用。</p>
<h4 id="so"><a href="#so" class="headerlink" title="so"></a>so</h4><p>App中C、C++代码编译生成的二进制文件，与手机的CPU架构相关，不同CPU架构生成的文件有些不同。开发中常常会生成多份文件，然后打包到Apk中，不同CPU类型，会调用不同的文件。</p>
<h4 id="resource"><a href="#resource" class="headerlink" title="resource"></a>resource</h4><p>Android中资源文件比较多，通常放在res和assets文件夹下面。常见的有布局、图片、字符、样式、主题等。</p>
<h4 id="安装路径"><a href="#安装路径" class="headerlink" title="安装路径"></a>安装路径</h4><p>上面的介绍的Apk结构，那么Apk安装以后，它的安装位置在哪，资源和数据又放在哪里呢？</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-f78caf0b6a8668fe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="安装路径"></p>
<p><code>/data/app/{package}/</code>主要放置Apk文件，同时Cpu对应的so文件也会被解压到对应的文件夹中，Android高级版本中还会对dex做优化，生成odex文件也在这个文件夹中。</p>
<p><code>data/data/{package}/</code>主要存放App生成的数据，比如SharedPreferences、cache等其他文件。</p>
<p>那么问题来了，如果调用为安装的Apk，假设能够运行，那么他们的运行文件放在哪里？代码中生成的数据文件又要放在哪里？</p>
<h3 id="App启动流程介绍"><a href="#App启动流程介绍" class="headerlink" title="App启动流程介绍"></a>App启动流程介绍</h3><p>App的二进制文件Apk安装以后，就可以直接启动了，直接点击Launcher上面的图片即可，但是我们需要的是一个App启动另外一个apk文件，所以有必要了解下App的启动流程。</p>
<h4 id="IPC-amp-Binder"><a href="#IPC-amp-Binder" class="headerlink" title="IPC &amp; Binder"></a>IPC &amp; Binder</h4><p>在Android系统中，每一个应用程序都是由一些Activity和Service组成的，这些Activity和Service有可能运行在同一个进程中，也有可能运行在不同的进程中。那么，不在同一个进程的Activity或者Service是如何通信的呢？</p>
<p>Android系统提供一种Binder机制，能够使进程之间相互通信。</p>
<p><a href="http://blog.csdn.net/luoshengyang/article/details/6618363" target="_blank" rel="external">Android进程间通信资料</a></p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-af48722dca715bea.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<h4 id="AMS"><a href="#AMS" class="headerlink" title="AMS"></a>AMS</h4><p>Activity启动流程说个一天也说不完，过程很长，也很繁琐，不过我们只要记住了AMS就可以了。</p>
<p><a href="http://blog.csdn.net/AllenWells/article/details/68926952" target="_blank" rel="external">Android系统应用框架篇：Activity启动流程</a></p>
<p>盗一张图</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-6821aef3d92c294a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="AMS"></p>
<h3 id="插件化技术问题与解决方案"><a href="#插件化技术问题与解决方案" class="headerlink" title="插件化技术问题与解决方案"></a>插件化技术问题与解决方案</h3><h4 id="代码加载"><a href="#代码加载" class="headerlink" title="代码加载"></a>代码加载</h4><p>按照正常思路，如果一个主Apk需要运行一个插件Apk，那么怎么样才能把里面的代码加载过来呢？</p>
<h5 id="Java-ClassLoader"><a href="#Java-ClassLoader" class="headerlink" title="Java ClassLoader"></a>Java ClassLoader</h5><p>Java中提供了ClassLoader方式来加载代码，然后就可以运行其中的代码了。这里有一份资料(<a href="http://blog.csdn.net/xyang81/article/details/7292380" target="_blank" rel="external">深入分析Java ClassLoader原理</a>) ，可以简单了解下。</p>
<ul>
<li>原理介绍</li>
</ul>
<p>ClassLoader使用的是双亲委托模型来搜索类的，每个ClassLoader实例都有一个父类加载器的引用（不是继承的关系，是一个包含的关系），虚拟机内置的类加载器（Bootstrap ClassLoader）本身没有父类加载器，但可以用作其它ClassLoader实例的的父类加载器。<br>当一个ClassLoader实例需要加载某个类时，它会试图亲自搜索某个类之前，先把这个任务委托给它的父类加载器，这个过程是由上至下依次检查的，首先由最顶层的类加载器Bootstrap ClassLoader试图加载，<br>如果没加载到，则把任务转交给Extension ClassLoader试图加载，如果也没加载到，则转交给App ClassLoader 进行加载，如果它也没有加载得到的话，则返回给委托的发起者，由它到指定的文件系统或网络等URL中加载该类。<br>如果它们都没有加载到这个类时，则抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义，并将它加载到内存当中，最后返回这个类在内存中的Class实例对象。</p>
<ul>
<li>为什么要使用双亲委托这种模型呢？</li>
</ul>
<p>因为这样可以避免重复加载，当父亲已经加载了该类的时候，就没有必要子ClassLoader再加载一次。<br>考虑到安全因素，我们试想一下，如果不使用这种委托模式，那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型，这样会存在非常大的安全隐患，而双亲委托的方式，就可以避免这种情况，<br>因为String已经在启动时就被引导类加载器（Bootstrcp ClassLoader）加载，所以用户自定义的ClassLoader永远也无法加载一个自己写的String，除非你改变JDK中ClassLoader搜索类的默认算法。</p>
<ul>
<li>但是JVM在搜索类的时候，又是如何判定两个class是相同的呢？</li>
</ul>
<p>JVM在判定两个class是否相同时，不仅要判断两个类名是否相同，而且要判断是否由同一个类加载器实例加载的。<br>只有两者同时满足的情况下，JVM才认为这两个class是相同的。就算两个class是同一份class字节码，如果被两个不同的ClassLoader实例所加载，JVM也会认为它们是两个不同class。<br>比如网络上的一个Java类org.classloader.simple.NetClassLoaderSimple，javac编译之后生成字节码文件NetClassLoaderSimple.class，ClassLoaderA和ClassLoaderB这两个类加载器并读取了NetClassLoaderSimple.class文件，<br>并分别定义出了java.lang.Class实例来表示这个类，对于JVM来说，它们是两个不同的实例对象，但它们确实是同一份字节码文件，如果试图将这个Class实例生成具体的对象进行转换时，<br>就会抛运行时异常java.lang.ClassCaseException，提示这是两个不同的类型。</p>
<h5 id="Android-ClassLoader"><a href="#Android-ClassLoader" class="headerlink" title="Android ClassLoader"></a>Android ClassLoader</h5><p>Android 的 Dalvik/ART 虚拟机如同标准 Java 的 JVM 虚拟机一样，也是同样需要加载 class 文件到内存中来使用，但是在 ClassLoader 的加载细节上会有略微的差别。</p>
<p><a href="http://jaeger.itscoder.com/android/2016/08/27/android-classloader.html" target="_blank" rel="external">热修复入门：Android 中的 ClassLoader</a>比较详细介绍了Android中ClassLoader。</p>
<p>在Android开发者官网上的<a href="https://developer.android.com/reference/java/lang/ClassLoader.html" target="_blank" rel="external">ClassLoader</a>的文档说明中我们可以看到，<br>ClassLoader是个抽象类，其具体实现的子类有 BaseDexClassLoader和SecureClassLoader。</p>
<p>SecureClassLoader的子类是URLClassLoader，其只能用来加载jar文件，这在Android的 Dalvik/ART 上没法使用的。</p>
<p>BaseDexClassLoader的子类是PathClassLoader和DexClassLoader 。</p>
<h6 id="PathClassLoader"><a href="#PathClassLoader" class="headerlink" title="PathClassLoader"></a>PathClassLoader</h6><p>PathClassLoader 在应用启动时创建，从<code>/data/app/{package}</code>安装目录下加载 apk 文件。</p>
<p>有2个构造函数，如下所示，这里遵从之前提到的双亲委托模型：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">PathClassLoader</span><span class="params">(String dexPath, ClassLoader parent)</span> </span>&#123;</div><div class="line">    <span class="keyword">super</span>(dexPath, <span class="keyword">null</span>, <span class="keyword">null</span>, parent);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">PathClassLoader</span><span class="params">(String dexPath, String libraryPath, ClassLoader parent)</span> </span>&#123;</div><div class="line">    <span class="keyword">super</span>(dexPath, <span class="keyword">null</span>, libraryPath, parent);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li><p>dexPath : 包含dex的jar文件或apk文件的路径集，多个以文件分隔符分隔，默认是“：”</p>
</li>
<li><p>libraryPath : 包含 C/C++ 库的路径集，多个同样以文件分隔符分隔，可以为空</p>
</li>
</ul>
<p>PathClassLoader 里面除了这2个构造方法以外就没有其他的代码了，具体的实现都是在 BaseDexClassLoader 里面，其dexPath比较受限制，一般是已经安装应用的 apk 文件路径。</p>
<p>在Android中，App安装到手机后，apk里面的class.dex中的class均是通过PathClassLoader来加载的。</p>
<h6 id="DexClassLoader"><a href="#DexClassLoader" class="headerlink" title="DexClassLoader"></a>DexClassLoader</h6><p>介绍 DexClassLoader 之前，先来看看其官方描述：</p>
<blockquote>
<p>A class loader that loads classes from .jar and .apk filescontaining a classes.dex entry. This can be used to execute code notinstalled as part of an application.</p>
</blockquote>
<p>很明显，对比 PathClassLoader 只能加载已经安装应用的dex或apk文件，DexClassLoader则没有此限制，可以从SD卡上加载包含class.dex的.jar和.apk 文件，这也是插件化和热修复的基础，在不需要安装应用的情况下，完成需要使用的dex的加载。</p>
<p>DexClassLoader 的源码里面只有一个构造方法，这里也是遵从双亲委托模型：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">DexClassLoader</span><span class="params">(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)</span> </span>&#123;</div><div class="line">    <span class="keyword">super</span>(dexPath, <span class="keyword">new</span> File(optimizedDirectory), libraryPath, parent);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>参数说明：</p>
<ul>
<li><p>String dexPath : 包含 class.dex 的 apk、jar 文件路径 ，多个用文件分隔符(默认是 ：)分隔</p>
</li>
<li><p>String optimizedDirectory : 用来缓存优化的 dex 文件的路径，即从 apk 或 jar 文件中提取出来的 dex 文件。该路径不可以为空，且应该是应用私有的，有读写权限的路径（实际上也可以使用外部存储空间</p>
</li>
<li><p>String libraryPath : 存储 C/C++ 库文件的路径集</p>
</li>
<li><p>ClassLoader parent : 父类加载器，遵从双亲委托模型</p>
</li>
</ul>
<h4 id="资源获取"><a href="#资源获取" class="headerlink" title="资源获取"></a>资源获取</h4><p>我们知道，Android Apk里面除了代码，剩下的就是资源，而且资源占了很大一部分空间，我们可以利用ClassLoader来加载代码，那么如何来加载apk中的资源，而且Android中的资源种类又可以分为很多种，比如布局、图片，字符、样式、主题等。</p>
<p>在组件中获取资源时使用getResource获得Resource对象，通过这个对象我们可以访问相关资源，比如文本、图片、颜色等。</p>
<p>通过跟踪源码发现，其实getResource方法是Context的一个抽象方法，getResource的实现是在ContextImp中实现的。<br>获取的Resource对象是应用的全局变量，然后继续跟踪源码，发现 Resource中有一个AssetManager的全局变量，在Resource的构造函数中传入的，所以最终获取资源都是通过AssetManager获取的，于是我们把注意力放到AssetManager上。</p>
<p>我们要解决下面两个问题。</p>
<p>一、如何获取AssetManager对象。</p>
<p>二、如何通过AssetManager对象获取插件中apk的资源。</p>
<p>通过对AssetManager的相关源码跟踪，我们找到答案。</p>
<p>一、AssetManager的构造函数没有对api公开，不能使用new创建；context.getAssets()可用获取当前上下文环境的 AssetManager；利用反射 AssetManager.class.newInstance()这样可用获取对象。</p>
<p>二、如何获取插件apk中的资源。我们发现AssetManager中有个重要的方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"><span class="comment"> * Add an additional set of assets to the asset manager.  This can be</span></div><div class="line"><span class="comment"> * either a directory or ZIP file.  Not for use by applications.  Returns</span></div><div class="line"><span class="comment"> * the cookie of the added asset, or 0 on failure.</span></div><div class="line"><span class="comment"> * &#123;<span class="doctag">@hide</span>&#125;</span></div><div class="line"><span class="comment"> */</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">addAssetPath</span><span class="params">(String path)</span> </span>&#123;</div><div class="line">    <span class="keyword">return</span>  addAssetPathInternal(path, <span class="keyword">false</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>我们可以把一个包含资源的文件包添加到assets中。这就是AssetManager查找资源的第一个路径。这个方法是一个隐藏方法，我们可以通过反射调用。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">AssetManager assetManager = AssetManager.class.newInstance() ; <span class="comment">// context .getAssets()？</span></div><div class="line">AssetManager.class.getDeclaredMethod(<span class="string">"addAssetPath"</span>, String.class).invoke(assetManager, apkPath);</div><div class="line">Resources pluginResources = <span class="keyword">new</span> Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());</div></pre></td></tr></table></figure>
<h4 id="Hook"><a href="#Hook" class="headerlink" title="Hook"></a>Hook</h4><p>Hook就是可以修改函数的调用，通常可以通过代理模式就可以达到修改的目的。</p>
<p>比如有个Java示例代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IService</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">void</span> <span class="title">fun</span><span class="params">()</span></span>;</div><div class="line">&#125;</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ServiceImpl</span> <span class="keyword">implements</span> <span class="title">IService</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"ServiceImpl"</span>;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fun</span><span class="params">()</span> </span>&#123;</div><div class="line">        Log.i(TAG, <span class="string">"fun: "</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>正常调用直接这样就可以了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> IService iService;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line">        setContentView(R.layout.activity_main);</div><div class="line"></div><div class="line">        iService = <span class="keyword">new</span> ServiceImpl();</div><div class="line">        callService();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">void</span> <span class="title">callService</span><span class="params">()</span> </span>&#123;</div><div class="line">        iService.fun();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>上面代码中MainActivity中含有iService字段，可以利用反射机制来替换它，然后当有其他地方调用iService的时候，就可以对调用方法进拦截和处理。</p>
<p>可以先实现自己的代理类，对需要Hook的地方添加下代码。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ServiceProxy</span> <span class="keyword">implements</span> <span class="title">IService</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"ServiceProxy"</span>;</div><div class="line"></div><div class="line">    <span class="meta">@NonNull</span></div><div class="line">    <span class="keyword">private</span> IService base;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ServiceProxy</span><span class="params">(@NonNull IService base)</span> </span>&#123;</div><div class="line">        <span class="keyword">this</span>.base = base;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fun</span><span class="params">()</span> </span>&#123;</div><div class="line">        Log.i(TAG, <span class="string">"fun: before"</span>);</div><div class="line">        base.fun();</div><div class="line">        Log.i(TAG, <span class="string">"fun: after"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>然后再修改MainActivity中的iService的值，首先获取iService字段的值，传给自己定义的Proxy对象，然后把Proxy对象再赋值给原先的iService字段，这样调用iService中方法的时候，就会执行Proxy的方法，然后由Proxy再进行处理。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">reflectHock</span><span class="params">()</span> </span>&#123;</div><div class="line">    <span class="keyword">try</span> &#123;</div><div class="line">        Class&lt;? extends MainActivity&gt; aClass = MainActivity.class;</div><div class="line">        Field field = aClass.getDeclaredField(<span class="string">"iService"</span>);</div><div class="line">        field.setAccessible(<span class="keyword">true</span>);</div><div class="line">        IService service = (IService) field.get(<span class="keyword">this</span>);</div><div class="line">        IService proxy = <span class="keyword">new</span> ServiceProxy(service);</div><div class="line">        field.set(<span class="keyword">this</span>, proxy);</div><div class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</div><div class="line">        e.printStackTrace();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>当然有时候，实现自己的Proxy类是很麻烦的，可以利用Java的动态代理技术来搞定。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyInvocationHandler</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"MyInvocationHandler"</span>;</div><div class="line"></div><div class="line">    <span class="meta">@NonNull</span></div><div class="line">    <span class="keyword">private</span> IService service;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyInvocationHandler</span><span class="params">(@NonNull IService service)</span> </span>&#123;</div><div class="line">        <span class="keyword">this</span>.service = service;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object o, Method method, Object[] objects)</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line">        Log.i(TAG, <span class="string">"invoke: before"</span>);</div><div class="line">        Object result = method.invoke(service, objects);</div><div class="line">        Log.i(TAG, <span class="string">"invoke: after"</span>);</div><div class="line">        <span class="keyword">return</span> result;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">proxyHook</span><span class="params">()</span> </span>&#123;</div><div class="line">    <span class="keyword">try</span> &#123;</div><div class="line">        Class&lt;? extends MainActivity&gt; aClass = MainActivity.class;</div><div class="line">        Field field = aClass.getDeclaredField(<span class="string">"iService"</span>);</div><div class="line">        field.setAccessible(<span class="keyword">true</span>);</div><div class="line">        IService value = (IService) field.get(<span class="keyword">this</span>);</div><div class="line"></div><div class="line">        InvocationHandler handler = <span class="keyword">new</span> MyInvocationHandler(value);</div><div class="line">        ClassLoader classLoader = value.getClass().getClassLoader();</div><div class="line">        Object instance = Proxy.newProxyInstance(classLoader, value.getClass().getInterfaces(), handler);</div><div class="line"></div><div class="line">        field.set(<span class="keyword">this</span>, instance);</div><div class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</div><div class="line">        e.printStackTrace();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="主流框架方案"><a href="#主流框架方案" class="headerlink" title="主流框架方案"></a>主流框架方案</h2><h3 id="Fragment加载"><a href="#Fragment加载" class="headerlink" title="Fragment加载"></a>Fragment加载</h3><p>早在2012年时候，出现了一个简单的Android插件化方案，原理大致这样的。</p>
<p>我们知道Android基本的页面元素是Activity，如果要动态加载一个界面，那么需要动态加载加载一个Activity，但是Activity是需要注册在Manifest中的。</p>
<p>所以就把目标瞄向了Fragment，首先Fragment是不需要注册的，使用的时候直接new出一个对象即可，然后放到了Activity容器中即可，那么能否从一个apk钟加载出来一个FragmentClass，然后使用反射实例化，然后放入到Activity中呢？</p>
<p>答案是可以的，首先在Manifest中定义个容器HostContainerActivity，然后页面跳转的时候通过intent，把目标的页面的fragment的class写成路径，<br>当 HostContainerActivity 页面启动，从intent中获取Fragment的路径，然后利用反射，动态new出一个示例放入到布局中即可。</p>
<p><a href="https://github.com/mmin18/AndroidDynamicLoader" target="_blank" rel="external">AndroidDynamicLoader</a>就是这样一个解决方案，但是这个方案是有限制的，所有的页面必须是Fragment，这样肯定不符合要求，所以这个方案就没有流行起来。</p>
<h3 id="Activity代理"><a href="#Activity代理" class="headerlink" title="Activity代理"></a>Activity代理</h3><p>上面说道了使用Fragment加载的形式，来显示插件中的页面，但是这个解决方案是有限制的，界面全部只能用Fragment，不能用Activity，不能称的上是一种完美的插件化解决方案。</p>
<p>那到底能不能用到Activity的方式，答案是肯定的。</p>
<p>可以这样，上面介绍了Fragment动态加载原理，我们把Fragment的路径换成Activity的路径，然后用原先的那个容器Activity，做为一个代理Activity，当HostContainerActivity启动时候，<br>初始化将要显示的Activity，然后当容器Activity依次执行对应的生命周期时候，容器Activity做一个代理Activity，也要相应执行动态加载的Activity。</p>
<p>大致代码示例如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HostContainerActivity</span> <span class="keyword">extends</span> <span class="title">BaseActivity</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String EXTRA_BASE_ACTIVITY = <span class="string">"extra_base_activity"</span>;</div><div class="line">    <span class="keyword">private</span> BaseActivity remote;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(@Nullable Bundle savedInstanceState)</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line"></div><div class="line">        String clazz = getIntent().getStringExtra(EXTRA_BASE_ACTIVITY);</div><div class="line">        <span class="keyword">try</span> &#123;</div><div class="line">            remote = (BaseActivity) Class.forName(clazz).newInstance();</div><div class="line">            remote.onCreate(savedInstanceState);</div><div class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</div><div class="line">            e.printStackTrace();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStart</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onStart();</div><div class="line"></div><div class="line">        remote.onStart();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResume</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onResume();</div><div class="line"></div><div class="line">        remote.onResume();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPause</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onPause();</div><div class="line"></div><div class="line">        remote.onPause();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStop</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onStop();</div><div class="line"></div><div class="line">        remote.onStop();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDestroy</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onDestroy();</div><div class="line"></div><div class="line">        remote.onDestroy();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><a href="https://github.com/singwhatiwanna/dynamic-load-apk" target="_blank" rel="external">dynamic-load-apk</a>  这个动态化框架就是利用这个原理来实现的。</p>
<p>但是这个方案还是有限制的，因为插件中的Activity并不是系统直接运行的，而是由另外一个Activity作为代理运行的，这个Activity不是一个真正的Activity，<br>很多的功能是限制的，比如需要在Activity弹出一个Toast，则是不行的，因为当前的Activity没有context，所以dynamic-load-apk提出了1个关键字——that，<br>java中this表示对象本身，但是本对象不能当做context使用，因为当前的Activity只是一个Java对象，而that是真正运行的Activity对象。</p>
<h3 id="Activity占坑"><a href="#Activity占坑" class="headerlink" title="Activity占坑"></a>Activity占坑</h3><p>上面介绍Activity代理的方法，虽然插件中可以正常使用Activity，但是限制还是很多，用起来很不方便。</p>
<p>那到底有没有最优解，既可以不需要注册Activity，又可以动态的加载Activity，答案是肯定的。我们可以来一个偷梁换柱，既然要注册咱们就先注册一个，然后启动的时候，<br>把需要的运行的Activity当做参数传递过去，让系统启动那个替身Activity，当时机恰当的时候，我们再把那个Activity的对象给换回来即可，这个叫做瞒天过海。</p>
<p>这里有一篇<a href="http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/" target="_blank" rel="external">文章</a>详细记载了Activity占坑方案是怎么运行的以及方案的原理。</p>
<h2 id="360RePlugin介绍"><a href="#360RePlugin介绍" class="headerlink" title="360RePlugin介绍"></a>360RePlugin介绍</h2><p>Ok，上面说了这么多，全部都是引子，下面着重介绍今天的主角——RePlugin。</p>
<p>RePlugin是一套完整的、稳定的、适合全面使用的，占坑类插件化方案，由360手机卫士的RePlugin Team研发，也是业内首个提出”全面插件化“（全面特性、全面兼容、全面使用）的方案。</p>
<h3 id="主要优势"><a href="#主要优势" class="headerlink" title="主要优势"></a>主要优势</h3><ul>
<li>极其灵活：</li>
</ul>
<p>主程序无需升级（无需在Manifest中预埋组件），即可支持新增的四大组件，甚至全新的插件</p>
<ul>
<li>非常稳定：</li>
</ul>
<p>Hook点仅有一处（ClassLoader），无任何Binder Hook！如此可做到其崩溃率仅为“万分之一”，并完美兼容市面上近乎所有的Android ROM</p>
<ul>
<li>特性丰富：</li>
</ul>
<p>支持近乎所有在“单品”开发时的特性。包括静态Receiver、Task-Affinity坑位、自定义Theme、进程坑位、AppCompat、DataBinding等</p>
<ul>
<li>易于集成：</li>
</ul>
<p>无论插件还是主程序，只需“数行”就能完成接入</p>
<ul>
<li>管理成熟：</li>
</ul>
<p>拥有成熟稳定的“插件管理方案”，支持插件安装、升级、卸载、版本管理，甚至包括进程通讯、协议版本、安全校验等</p>
<ul>
<li>数亿支撑：</li>
</ul>
<p>有360手机卫士庞大的数亿用户做支撑，三年多的残酷验证，确保App用到的方案是最稳定、最适合使用的</p>
<h3 id="集成与Demo演示"><a href="#集成与Demo演示" class="headerlink" title="集成与Demo演示"></a>集成与Demo演示</h3><p>集成也非常简单，比如有2个工程，一个是主工程host，一个是插件工程sub。</p>
<p>本人写作的时候，RePlugin版本为<code>2.1.5</code>，可能会与最新版本不一致。</p>
<ul>
<li>添加Host根目录Gradle依赖</li>
</ul>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">buildscript</span> &#123;</div><div class="line">    <span class="keyword">repositories</span> &#123;</div><div class="line">        jcenter()</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">dependencies</span> &#123;</div><div class="line">        <span class="keyword">classpath</span> <span class="string">'com.android.tools.build:gradle:2.3.3'</span></div><div class="line"></div><div class="line">        <span class="comment">// <span class="doctag">NOTE:</span> Do not place your application dependencies here; they belong</span></div><div class="line">        <span class="comment">// in the individual module build.gradle files</span></div><div class="line">        <span class="keyword">classpath</span> <span class="string">'com.qihoo360.replugin:replugin-host-gradle:2.1.5'</span></div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>添加Host项目Gradle依赖</li>
</ul>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div></pre></td><td class="code"><pre><div class="line">apply plugin: <span class="string">'com.android.application'</span></div><div class="line">apply plugin: <span class="string">'replugin-host-gradle'</span></div><div class="line"></div><div class="line">android &#123;</div><div class="line">    compileSdkVersion <span class="number">26</span></div><div class="line">    buildToolsVersion <span class="string">"26.0.0"</span></div><div class="line">    defaultConfig &#123;</div><div class="line">        applicationId <span class="string">"cn.mycommons.replugindemo"</span></div><div class="line">        minSdkVersion <span class="number">15</span></div><div class="line">        targetSdkVersion <span class="number">26</span></div><div class="line">        versionCode <span class="number">1</span></div><div class="line">        versionName <span class="string">"1.0"</span></div><div class="line">        testInstrumentationRunner <span class="string">"android.support.test.runner.AndroidJUnitRunner"</span></div><div class="line">    &#125;</div><div class="line">    buildTypes &#123;</div><div class="line">        release &#123;</div><div class="line">            minifyEnabled <span class="keyword">false</span></div><div class="line">            proguardFiles getDefaultProguardFile(<span class="string">'proguard-android.txt'</span>), <span class="string">'proguard-rules.pro'</span></div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">repluginHostConfig &#123;</div><div class="line">    useAppCompat = <span class="keyword">true</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">dependencies</span> &#123;</div><div class="line">    <span class="keyword">compile</span> <span class="keyword">fileTree</span>(dir: <span class="string">'libs'</span>, <span class="keyword">include</span>: [<span class="string">'*.jar'</span>])</div><div class="line">    <span class="keyword">compile</span> <span class="string">'com.android.support:appcompat-v7:26.+'</span></div><div class="line">    <span class="keyword">compile</span> <span class="string">'com.android.support.constraint:constraint-layout:1.0.2'</span></div><div class="line">    <span class="keyword">compile</span> <span class="string">'com.qihoo360.replugin:replugin-host-lib:2.1.5'</span></div><div class="line"></div><div class="line">    testCompile <span class="string">'junit:junit:4.12'</span></div><div class="line">    androidTestCompile(<span class="string">'com.android.support.test.espresso:espresso-core:2.2.2'</span>, &#123;</div><div class="line">        <span class="keyword">exclude</span> <span class="keyword">group</span>: <span class="string">'com.android.support'</span>, module: <span class="string">'support-annotations'</span></div><div class="line">    &#125;)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>添加Sub根目录Gradle依赖</li>
</ul>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">buildscript</span> &#123;</div><div class="line">    <span class="keyword">repositories</span> &#123;</div><div class="line">        jcenter()</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">dependencies</span> &#123;</div><div class="line">        <span class="keyword">classpath</span> <span class="string">'com.android.tools.build:gradle:2.3.3'</span></div><div class="line"></div><div class="line">        <span class="comment">// <span class="doctag">NOTE:</span> Do not place your application dependencies here; they belong</span></div><div class="line">        <span class="comment">// in the individual module build.gradle files</span></div><div class="line">        <span class="keyword">classpath</span> <span class="string">'com.qihoo360.replugin:replugin-plugin-gradle:2.1.5'</span></div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>添加Sub项目Gradle依赖</li>
</ul>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div></pre></td><td class="code"><pre><div class="line">apply plugin: <span class="string">'com.android.application'</span></div><div class="line">apply plugin: <span class="string">'replugin-plugin-gradle'</span></div><div class="line"></div><div class="line">android &#123;</div><div class="line">    compileSdkVersion <span class="number">26</span></div><div class="line">    buildToolsVersion <span class="string">"26.0.0"</span></div><div class="line"></div><div class="line">    defaultConfig &#123;</div><div class="line">        applicationId <span class="string">"cn.mycommons.repluginsdemo.sub"</span></div><div class="line">        minSdkVersion <span class="number">15</span></div><div class="line">        targetSdkVersion <span class="number">26</span></div><div class="line">        versionCode <span class="number">1</span></div><div class="line">        versionName <span class="string">"1.0"</span></div><div class="line"></div><div class="line">        testInstrumentationRunner <span class="string">"android.support.test.runner.AndroidJUnitRunner"</span></div><div class="line"></div><div class="line">    &#125;</div><div class="line">    buildTypes &#123;</div><div class="line">        release &#123;</div><div class="line">            minifyEnabled <span class="keyword">false</span></div><div class="line">            proguardFiles getDefaultProguardFile(<span class="string">'proguard-android.txt'</span>), <span class="string">'proguard-rules.pro'</span></div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">repluginPluginConfig &#123;</div><div class="line">    <span class="comment">//插件名</span></div><div class="line">    pluginName = <span class="string">"app"</span></div><div class="line">    <span class="comment">//宿主app的包名</span></div><div class="line">    hostApplicationId = <span class="string">"cn.mycommons.replugindemo"</span></div><div class="line">    <span class="comment">//宿主app的启动activity</span></div><div class="line">    hostAppLauncherActivity = <span class="string">"cn.mycommons.replugindemo.MainActivity"</span></div><div class="line"></div><div class="line">    <span class="comment">// Name of 'App Module'，use '' if root dir is 'App Module'. ':app' as default.</span></div><div class="line">    appModule = <span class="string">':app'</span></div><div class="line"></div><div class="line">    <span class="comment">// Injectors ignored</span></div><div class="line">    <span class="comment">// LoaderActivityInjector: Replace Activity to LoaderActivity</span></div><div class="line">    <span class="comment">// ProviderInjector: Inject provider method call.</span></div><div class="line">    <span class="comment">// ignoredInjectors = ['LoaderActivityInjector']</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">dependencies</span> &#123;</div><div class="line">    <span class="keyword">compile</span> <span class="keyword">fileTree</span>(dir: <span class="string">'libs'</span>, <span class="keyword">include</span>: [<span class="string">'*.jar'</span>])</div><div class="line"></div><div class="line">    <span class="keyword">compile</span> <span class="string">'com.android.support:appcompat-v7:26.+'</span></div><div class="line">    <span class="keyword">compile</span> <span class="string">'com.android.support.constraint:constraint-layout:1.0.2'</span></div><div class="line">    <span class="keyword">compile</span> <span class="string">'com.qihoo360.replugin:replugin-plugin-lib:2.1.5'</span></div><div class="line"></div><div class="line">    testCompile <span class="string">'junit:junit:4.12'</span></div><div class="line">    androidTestCompile(<span class="string">'com.android.support.test.espresso:espresso-core:2.2.2'</span>, &#123;</div><div class="line">        <span class="keyword">exclude</span> <span class="keyword">group</span>: <span class="string">'com.android.support'</span>, module: <span class="string">'support-annotations'</span></div><div class="line">    &#125;)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="原理介绍"><a href="#原理介绍" class="headerlink" title="原理介绍"></a>原理介绍</h3><p>RePlugin源码主要分为4部分，对比其他插件化，它的强大和特色，在于它只Hook住了ClassLoader。One Hook这个坚持，最大程度保证了稳定性、兼容性和可维护性。</p>
<h4 id="host-lib"><a href="#host-lib" class="headerlink" title="host lib"></a>host lib</h4><p>插件宿主库，主要是对插件的管理，以及对ClassLoader的Hook，具体原理和管理逻辑不做详细解释。</p>
<h4 id="host-gradle"><a href="#host-gradle" class="headerlink" title="host gradle"></a>host gradle</h4><p>对插件宿主代码编译过程进行处理，主要有config.json文件生成、RePluginHostConfig.java代码生成、以及Activity坑位代码插入到Manifest中。</p>
<p>比如我们内置一个插件，按照官方文档，这样操作的。</p>
<ul>
<li><p>将APK改名为：[插件名].jar</p>
</li>
<li><p>放入主程序的assets/plugins目录</p>
</li>
</ul>
<p>我们可以看看Host apk中包含哪些资源。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-7c8316e2a4fdf48c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p>插件自动生成了plugin-builtin.json文件</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-1cd50f773eacea05.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p>同时也在Manifest中插入很多坑位。</p>
<p><img src="https://raw.githubusercontent.com/LiushuiXiaoxia/AndroidPluginArticle/master/doc/903.png" alt=""></p>
<p>RePluginHostConfig.java代码生成逻辑。</p>
<h4 id="plugin-lib"><a href="#plugin-lib" class="headerlink" title="plugin lib"></a>plugin lib</h4><p>同宿主库一样，这个是给插件App提供基本的支持。</p>
<h4 id="plugin-gradle"><a href="#plugin-gradle" class="headerlink" title="plugin gradle"></a>plugin gradle</h4><p>对插件App代码编译过程进行处理，主要修改插件中四大组建的父类，没错，就是这样。</p>
<p>比如有个<code>LoginActivity</code>，它是继承<code>Activity</code>的，那么会修改它的父类为<code>PluginActivity</code>，如果是<code>AppCompatActivity</code>，那么会替换成<code>PluginAppCompatActivity</code></p>
<p>如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line"></div><div class="line">        setContentView(R.layout.activity_main);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>反编译Apk可以看到修改后的结果。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-44dd1a8a5e8dfa44.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p>源码里面也有体现</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1520343-b33e1015da5c2652.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<h2 id="其他插件化方案"><a href="#其他插件化方案" class="headerlink" title="其他插件化方案"></a>其他插件化方案</h2><p>上次大致是RePlugin的原理，当然除了RePlugin的解决方案以外，还有其他几家厂商的解决方案。</p>
<h3 id="Instant-App"><a href="#Instant-App" class="headerlink" title="Instant App"></a>Instant App</h3><p><a href="https://developer.android.com/topic/instant-apps/index.html" target="_blank" rel="external">Android Instant App 官网</a></p>
<p>16年IO的时候，Google提出了Instant App特性，在17年IO正式发布这项技术，不过这项技术在我写这篇文章的时候，还是beta版本。</p>
<p>它的使用方式很简单，你在 Android 手机上，朋友给你发来一个链接，比方说一家外卖店面。而恰好外卖App应用也支持了 Instant Apps。你点击了这个链接，就直接进入了外卖应用，即便手机并没有安装它。</p>
<p>实现原理大致是利用App linker唤起打开app的intent，Google Play检测到支持该intent,而且没有安装后，直接通过类似Android插件化的原理，打开相关页面。</p>
<p>但是这个Instant App必须发布在Google Play上， 国内暂时没有办法使用。</p>
<h3 id="淘宝Atlas"><a href="#淘宝Atlas" class="headerlink" title="淘宝Atlas"></a>淘宝Atlas</h3><p><a href="https://github.com/alibaba/atlas" target="_blank" rel="external">淘宝Atlas</a></p>
<p>Atlas是伴随着手机淘宝的不断发展而衍生出来的一个运行于Android系统上的一个容器化框架，我们也叫动态组件化(Dynamic Bundle)框架。它主要提供了解耦化、组件化、动态性的支持。覆盖了工程师的工程编码期、Apk运行期以及后续运维期的各种问题。</p>
<p>在工程期，实现工程独立开发，调试的功能，工程模块可以独立。</p>
<p>在运行期，实现完整的组件生命周期的映射，类隔离等机制。</p>
<p>在运维期，提供快速增量的更新修复能力，快速升级。</p>
<p>Atlas是工程期和运行期共同起作用的框架，我们尽量将一些工作放到工程期，这样保证运行期更简单，更稳定。</p>
<p>相比multidex，atlas在解决了方法数限制的同时以OSGI为参考，明确了业务开发的边界，使得业务在满足并行迭代，快速开发的同时，能够进行灵活发布，动态更新以及提供了线上故障快速修复的能力。</p>
<p>与外界某些插件框架不同的是，atlas是一个组件框架，atlas不是一个多进程的框架，他主要完成的就是在运行环境中按需地去完成各个bundle的安装，加载类和资源。</p>
<h3 id="滴滴VirtualAPK"><a href="#滴滴VirtualAPK" class="headerlink" title="滴滴VirtualAPK"></a>滴滴VirtualAPK</h3><p><a href="https://github.com/didi/VirtualAPK" target="_blank" rel="external">VirtualAPK</a></p>
<p><a href="http://geek.csdn.net/news/detail/130917" target="_blank" rel="external">VirtualAPK介绍</a></p>
<p>VirtualAPK是滴滴17年开源出来的一款插件化方案。</p>
<h3 id="Small"><a href="#Small" class="headerlink" title="Small"></a>Small</h3><p><a href="https://github.com/wequick/Small" target="_blank" rel="external">Small</a></p>
<blockquote>
<p>世界那么大，组件那么小。Small，做最轻巧的跨平台插件化框架。 ——Galenlin</p>
</blockquote>
<p>这是Small作者，林光亮老师，给Small一句概括。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文只是简单的介绍下插件化相关内容，很多内容也是参照大神的博客的，感觉80%都是从别人那边复制过来的，同时插件不只是简单的加载界面和资源，包括BroadCastReceiver、Service等组件使用。</p>
<p>RePlugin使用方法还是蛮简单的，大部分情况下，插件的开发，相当于单独的一个App开发。</p>
<p>相对于其他厂商的方案，个人比较偏向于RePlugin，主要是因为开发简单，比较稳定，Hook点少，支持特性较多等。</p>
<h2 id="相关资料"><a href="#相关资料" class="headerlink" title="相关资料"></a>相关资料</h2><p><a href="https://github.com/LiushuiXiaoxia/AndroidModular" target="_blank" rel="external">关于Android模块化我有一些话不知当讲不当讲</a></p>
<p><a href="http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/" target="_blank" rel="external">Android插件化原理解析——Hook机制之动态代理</a></p>
<p><a href="http://blog.csdn.net/bupt073114/article/details/42298337" target="_blank" rel="external">APK文件结构和安装过程</a></p>
<p><a href="http://blog.csdn.net/luoshengyang/article/details/6618363" target="_blank" rel="external">Android进程间通信资料</a></p>
<p><a href="http://blog.csdn.net/AllenWells/article/details/68926952" target="_blank" rel="external">Android系统应用框架篇：Activity启动流程</a></p>
<p><a href="http://weishu.me/2016/03/07/understand-plugin-framework-ams-pms-hook/" target="_blank" rel="external">Android 插件化原理解析——Hook机制之AMS&amp;PMS</a></p>
<p><a href="http://blog.csdn.net/xyang81/article/details/7292380" target="_blank" rel="external">深入分析Java ClassLoader原理</a></p>
<p><a href="http://jaeger.itscoder.com/android/2016/08/27/android-classloader.html" target="_blank" rel="external">热修复入门：Android中的ClassLoader</a></p>
<p><a href="http://www.liuguangli.win/archives/370" target="_blank" rel="external">ANDROID应用程序插件化研究之ASSETMANAGER</a></p>
<p><a href="https://github.com/Qihoo360/DroidPlugin" target="_blank" rel="external">DroidPlugin</a></p>
<p><a href="https://github.com/CtripMobile/DynamicAPK" target="_blank" rel="external">DynamicAPK</a></p>
<p><a href="https://github.com/mmin18/AndroidDynamicLoader" target="_blank" rel="external">AndroidDynamicLoader，利用动态加载Fragment来解决</a></p>
<p><a href="https://github.com/singwhatiwanna/dynamic-load-apk" target="_blank" rel="external">dynamic-load-apk</a></p>
<p><a href="https://github.com/houkx/android-pluginmgr" target="_blank" rel="external">android-pluginmgr</a></p>
<p><a href="https://github.com/wequick/Small" target="_blank" rel="external">Small</a></p>
<p><a href="https://github.com/CtripMobile/DynamicAPK" target="_blank" rel="external">DynamicAPK</a></p>
<p><a href="https://github.com/alibaba/atlas" target="_blank" rel="external">淘宝Atlas</a></p>
<p><a href="https://github.com/didi/VirtualAPK" target="_blank" rel="external">VirtualAPK</a></p>
<p><a href="http://geek.csdn.net/news/detail/130917" target="_blank" rel="external">VirtualAPK介绍</a></p>
<p><a href="https://developer.android.com/topic/instant-apps/index.html" target="_blank" rel="external">Android Instant App 官网</a></p>

      
    </div>
    
    
    

    

    
      <div>
        <div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
  <div>坚持原创技术分享，您的支持将鼓励我继续创作！</div>
  <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
    <span>打赏</span>
  </button>
  <div id="QR" style="display: none;">

    
      <div id="wechat" style="display: inline-block">
        <img id="wechat_qr" src="/images/wechat_pay.jpeg" alt="流水不腐小夏 微信支付"/>
        <p>微信支付</p>
      </div>
    

    
      <div id="alipay" style="display: inline-block">
        <img id="alipay_qr" src="/images/alipay.jpeg" alt="流水不腐小夏 支付宝"/>
        <p>支付宝</p>
      </div>
    

    

  </div>
</div>

      </div>
    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Android插件化/" rel="tag"># Android插件化</a>
          
            <a href="/tags/Replugin/" rel="tag"># Replugin</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2017/10/24/MongoDB-Query/" rel="next" title="MongoDB查询总结">
                <i class="fa fa-chevron-left"></i> MongoDB查询总结
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2017/11/19/Spring-Cache-Redis/" rel="prev" title="Spring Cache与Redis结合使用">
                Spring Cache与Redis结合使用 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
      <div id="disqus_thread">
        <noscript>
          Please enable JavaScript to view the
          <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a>
        </noscript>
      </div>
    </div>

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/images/avatar.jpeg"
                alt="流水不腐小夏" />
            
              <p class="site-author-name" itemprop="name">流水不腐小夏</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">31</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">10</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">55</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          <div class="links-of-author motion-element">
            
              
                <span class="links-of-author-item">
                  <a href="https://github.com/LiushuiXiaoxia" target="_blank" title="GitHub">
                    
                      <i class="fa fa-fw fa-GitHub"></i>GitHub</a>
                </span>
              
                <span class="links-of-author-item">
                  <a href="http://www.jianshu.com/u/58771f980bac" target="_blank" title="简书">
                    
                      <i class="fa fa-fw fa-简书"></i>简书</a>
                </span>
              
                <span class="links-of-author-item">
                  <a href="http://blog.csdn.net/guijiaoba" target="_blank" title="CSDN">
                    
                      <i class="fa fa-fw fa-CSDN"></i>CSDN</a>
                </span>
              
            
          </div>

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#是时候来一波Android插件化了"><span class="nav-number">1.</span> <span class="nav-text">是时候来一波Android插件化了</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#前言"><span class="nav-number">1.1.</span> <span class="nav-text">前言</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Android开发演进"><span class="nav-number">1.2.</span> <span class="nav-text">Android开发演进</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#模块化介绍"><span class="nav-number">1.3.</span> <span class="nav-text">模块化介绍</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#插件化介绍"><span class="nav-number">1.4.</span> <span class="nav-text">插件化介绍</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#前提技术介绍"><span class="nav-number">1.5.</span> <span class="nav-text">前提技术介绍</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#APK构成"><span class="nav-number">1.5.1.</span> <span class="nav-text">APK构成</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Manifest"><span class="nav-number">1.5.1.1.</span> <span class="nav-text">Manifest</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Application"><span class="nav-number">1.5.1.2.</span> <span class="nav-text">Application</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#四大组件"><span class="nav-number">1.5.1.3.</span> <span class="nav-text">四大组件</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#so"><span class="nav-number">1.5.1.4.</span> <span class="nav-text">so</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#resource"><span class="nav-number">1.5.1.5.</span> <span class="nav-text">resource</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#安装路径"><span class="nav-number">1.5.1.6.</span> <span class="nav-text">安装路径</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#App启动流程介绍"><span class="nav-number">1.5.2.</span> <span class="nav-text">App启动流程介绍</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#IPC-amp-Binder"><span class="nav-number">1.5.2.1.</span> <span class="nav-text">IPC & Binder</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#AMS"><span class="nav-number">1.5.2.2.</span> <span class="nav-text">AMS</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#插件化技术问题与解决方案"><span class="nav-number">1.5.3.</span> <span class="nav-text">插件化技术问题与解决方案</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#代码加载"><span class="nav-number">1.5.3.1.</span> <span class="nav-text">代码加载</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#Java-ClassLoader"><span class="nav-number">1.5.3.1.1.</span> <span class="nav-text">Java ClassLoader</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#Android-ClassLoader"><span class="nav-number">1.5.3.1.2.</span> <span class="nav-text">Android ClassLoader</span></a><ol class="nav-child"><li class="nav-item nav-level-6"><a class="nav-link" href="#PathClassLoader"><span class="nav-number">1.5.3.1.2.1.</span> <span class="nav-text">PathClassLoader</span></a></li><li class="nav-item nav-level-6"><a class="nav-link" href="#DexClassLoader"><span class="nav-number">1.5.3.1.2.2.</span> <span class="nav-text">DexClassLoader</span></a></li></ol></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#资源获取"><span class="nav-number">1.5.3.2.</span> <span class="nav-text">资源获取</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Hook"><span class="nav-number">1.5.3.3.</span> <span class="nav-text">Hook</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#主流框架方案"><span class="nav-number">1.6.</span> <span class="nav-text">主流框架方案</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Fragment加载"><span class="nav-number">1.6.1.</span> <span class="nav-text">Fragment加载</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Activity代理"><span class="nav-number">1.6.2.</span> <span class="nav-text">Activity代理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Activity占坑"><span class="nav-number">1.6.3.</span> <span class="nav-text">Activity占坑</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#360RePlugin介绍"><span class="nav-number">1.7.</span> <span class="nav-text">360RePlugin介绍</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#主要优势"><span class="nav-number">1.7.1.</span> <span class="nav-text">主要优势</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#集成与Demo演示"><span class="nav-number">1.7.2.</span> <span class="nav-text">集成与Demo演示</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#原理介绍"><span class="nav-number">1.7.3.</span> <span class="nav-text">原理介绍</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#host-lib"><span class="nav-number">1.7.3.1.</span> <span class="nav-text">host lib</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#host-gradle"><span class="nav-number">1.7.3.2.</span> <span class="nav-text">host gradle</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#plugin-lib"><span class="nav-number">1.7.3.3.</span> <span class="nav-text">plugin lib</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#plugin-gradle"><span class="nav-number">1.7.3.4.</span> <span class="nav-text">plugin gradle</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#其他插件化方案"><span class="nav-number">1.8.</span> <span class="nav-text">其他插件化方案</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Instant-App"><span class="nav-number">1.8.1.</span> <span class="nav-text">Instant App</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#淘宝Atlas"><span class="nav-number">1.8.2.</span> <span class="nav-text">淘宝Atlas</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#滴滴VirtualAPK"><span class="nav-number">1.8.3.</span> <span class="nav-text">滴滴VirtualAPK</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Small"><span class="nav-number">1.8.4.</span> <span class="nav-text">Small</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#总结"><span class="nav-number">1.9.</span> <span class="nav-text">总结</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#相关资料"><span class="nav-number">1.10.</span> <span class="nav-text">相关资料</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; 2015 &mdash; <span itemprop="copyrightYear">2017</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">流水不腐小夏</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.3</div>




        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  

  
</div>








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.3"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.3"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.3"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.3"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.3"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.3"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.3"></script>



  


  

    
      <script id="dsq-count-scr" src="https://mycommons-cn.disqus.com/count.js" async></script>
    

    
      <script type="text/javascript">
        var disqus_config = function () {
          this.page.url = 'http://mycommons.cn/2017/10/25/Android-RePlugin/';
          this.page.identifier = '2017/10/25/Android-RePlugin/';
          this.page.title = '是时候来一波Android插件化了';
        };
        var d = document, s = d.createElement('script');
        s.src = 'https://mycommons-cn.disqus.com/embed.js';
        s.setAttribute('data-timestamp', '' + +new Date());
        (d.head || d.body).appendChild(s);
      </script>
    

  




	





  














  




  
  
  
  <link rel="stylesheet" href="/lib/algolia-instant-search/instantsearch.min.css">

  
  
  <script src="/lib/algolia-instant-search/instantsearch.min.js"></script>
  

  <script src="/js/src/algolia-search.js?v=5.1.3"></script>



  

  

  
  

  
  
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
    </script>

    <script type="text/x-mathjax-config">
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for (i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="//cdn.bootcss.com/mathjax/2.7.1/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
  


  

  

</body>
</html>
